unit Models;

interface

uses
  Windows, SysUtils, Types, StdCtrls, Classes, Messages,
  Skin;

type
  TMetaKey = type Integer;

const
  mkSHIFT = 1;
  mkCTRL  = 2;
  mkALT   = 4;

type
  TMouseKey =
    (mouseNone = 1,
     mouseL = 2,
     mouseR = 3,
     mouseM = 4
    );
  TMouseKeys = set of TMouseKey;
  TMouseEventType =
    ({Single Click}
     meSINGLEDOWN = $0011,
     meSINGLEUP = $0012,
     meSINGLEDELAYED = $0013,
     {Double Click}
     meDOUBLEDOWN = $0021,
     meDOUBLEUP = $0022,
     {Drag Drop}
     meDRAGSTART = $0100,
     meDRAGOVER = $0200,
     meDRAGDROP = $0300
    );

function ShiftState2MetaKey (Shift: TShiftState): TMetaKey;
function Keys2MetaKey (Keys: Integer): TMetaKey;
function Msg2MouseKey (Msg: Cardinal): TMouseKey;
function Keys2MouseKeys (Keys: Integer): TMouseKeys;

type
  TFileList = class;
  TLishData = type Pointer;

  IMainForm = interface
    ['{1EA5A32A-5741-4E29-8FC4-7BD219B1EBDE}']
    function Handle: HWND;
    function CmdView: TEdit;
    function FileView: TListBox;
    procedure StartMouseTimer (N: Integer);
    procedure EndMouseTimer (N: Integer);

    function TargetFiles: TWideStringDynArray;

    procedure Close;
    procedure ContextMenu;
    procedure DragDropContextMenu;
    procedure DragStart;
    procedure SetDragDropEffect(dwEffect: Integer);

    function FileList: TFileList;
    procedure SetFileList (FL: TFileList);
    procedure CommitFileList;

    function Skin: TSkin;
    procedure SetSkin (aSkin: TSkin);

    function Lish: TLishData;

    {Events}
    procedure DoCommandEvent (Cmd: WideString);
    procedure DoKeyEvent (Key: Word; Shift: TShiftState);
    procedure DoMouseEvent (Event: TMouseEventType;
                            MouseKey: TMouseKey; MetaKey: TMetaKey; X, Y: Integer);
    procedure DoContextMenu (X, Y: Integer);
  end;

  TTextlet = record
    top: Integer;
    left: Integer;
    width: Integer;
    font: Integer;
    text: WideString;
  end;
  TTextlets = array of TTextlet;
  TTextletsSet = array of TTextlets;

  TFileList = class
  public
    constructor Create (path, cmd: WideString; cnt: Integer);
    procedure AddFile (fname: WideString; cnt: Integer);
    procedure AddTextlet (_top, _left, _width, _font: Integer; _text: WideString);
    procedure Commit;
  private
    fPath: WideString;
    fCmd: WideString;
    fCount: Integer;
    fFiles: TWideStringDynArray;
    fFileRecs: TTextletsSet;
    fFileRecIdx: Integer;
    fIndex: Integer;
    fSelected: TBooleanDynArray;
    fSelectedCount: Integer;
    function GetTextletsCount (idx: Integer): Integer;
    function GetSelected (idx: Integer): Boolean;
    procedure SetSelected (idx: Integer; b: Boolean);
  public
    property PathName: WideString read fPath;
    property Command: WideString read fCmd;
    property ItemIndex: Integer read fIndex write fIndex;
    property Count: Integer read fCount;
    property FileName: TWideStringDynArray read fFiles;
    property Textlets: TTextletsSet read fFileRecs;
    property TextletsCount [idx: Integer]: Integer read GetTextletsCount;
    property Selected[Idx: Integer]: Boolean read GetSelected write SetSelected;
    property SelectedCount: Integer read fSelectedCount;
    function SelectedIndices: TIntegerDynArray;
    function SelectedFileNames: TWideStringDynArray;
  end;

implementation

function ShiftState2MetaKey (Shift: TShiftState): TMetaKey;
  begin
    Result := 0;
    if ssShift in Shift then Result := Result or mkSHIFT;
    if ssCtrl  in Shift then Result := Result or mkCTRL;
    if ssAlt   in Shift then Result := Result or mkALT;
  end;

function Keys2MetaKey (Keys: Integer): TMetaKey;
  begin
    Result := 0;
    if Keys and MK_SHIFT = MK_SHIFT then Result := Result or mkShift;
    if Keys and MK_CONTROL = MK_CONTROL then Result := Result or mkCTRL;
  end;

function Msg2MouseKey (Msg: Cardinal): TMouseKey;
  begin
    case Msg of
    WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK:
      Result := mouseL;
    WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK:
      Result := mouseR;
    WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK:
      Result := mouseM;
    else
      Result := mouseNone;
    end;
  end;

function Keys2MouseKeys (Keys: Integer): TMouseKeys;
  begin
    Result := [];
    if Keys and MK_LBUTTON = MK_LBUTTON then
      Include(Result, mouseL);
    if Keys and MK_RBUTTON = MK_RBUTTON then
      Include(Result, mouseR);
    if Keys and MK_MBUTTON = MK_MBUTTON then
      Include(Result, mouseM);
  end;

(*
 * TFileList
 *)
constructor TFileList.Create (path, cmd: WideString; cnt: Integer);
  var I: Integer;
  begin
    Inherited Create;
    fPath := path;
    fCmd := cmd;
    SetLength(fFiles, cnt);
    SetLength(fFileRecs, cnt);
    SetLength(fSelected, cnt);
    for I := 0 to cnt - 1 do fSelected[I] := False;
    fCount := -1;
    fSelectedCount := 0;
  end;

procedure TFileList.AddFile (fname: WideString; cnt: Integer);
  begin
    Inc(fCount);
    fFiles[fCount] := fname;
    SetLength(fFileRecs[fCount], cnt);
    fFileRecIdx := -1;
  end;

procedure TFileList.AddTextlet (_top, _left, _width, _font: Integer; _text: WideString);
  begin
    Inc(fFileRecIdx);
    with fFileRecs[fCount][fFileRecIdx] do begin
      top := _top;
      left := _left;
      width := _width;
      font := _font;
      text := _text;
    end;
  end;

procedure TFileList.Commit;
  begin
    fFileRecIdx := 0;
    Inc(fCount);
  end;

function TFileList.GetTextletsCount (idx: Integer): Integer;
  begin
    Result := Length(fFileRecs[idx]);
  end;

function TFileList.GetSelected (idx: Integer): Boolean;
  begin
    Result := fSelected[idx];
  end;

procedure TFileList.SetSelected (idx: Integer; b: Boolean);
  begin
    if b <> fSelected[idx] then
      if b then
        Inc(fSelectedCount)
      else
        Dec(fSelectedCount);
    fSelected[idx] := b;
  end;

function TFileList.SelectedIndices: TIntegerDynArray;
  var
    I, K: Integer;
  begin
    SetLength(Result, fCount);
    K := 0;
    for I := 0 to fCount - 1 do begin
      if fSelected[I] then begin
        Result[K] := I;
        Inc(K);
      end;
    end;
    SetLength(Result, K);
  end;

function TFileList.SelectedFileNames: TWideStringDynArray;
  var
    I, K: Integer;
  begin
    SetLength(Result, fCount);
    K := 0;
    for I := 0 to fCount - 1 do begin
      if fSelected[I] then begin
        Result[K] := fFiles[I];
        Inc(K);
      end;
    end;
    SetLength(Result, K);
  end;

end.

